/**
 * MIT License
 * 
 * Copyright (c) 2024 - present @ ebraid
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include <stm32f1.h>
#include <config.h>
#include <mm/mem.h>

/**
 * SYSCLOCK = CLOCK_PLL_OUT_HZ
 * AHB_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_AHB_PRE_DIV)
 * APB1_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_APB1_PRE_DIV)
 * APB2_HZ = CLOCK_PLL_OUT_HZ / (CLOCK_APB2_PRE_DIV)
 * 
 * I2S_HZ = CLOCK_PLL_OUT_HZ
 * 
 * AHB MAX 72MHz
 * 
 * APB1 MAX 36MHz
 * APB2 MAX 72MHz
 * 
 *                                  +-----> I2S2 / I2S3 (72MHz)
 *                                  |
 *                  +--------+      |   
 * HSE_OSC -------> |  PLL   | -----------> SYSCLK (72MHz)
 *                  +--------+      |
 *                                  |
 *                                  +-----> AHB  (72MHz)
 *                                  |
 *                                  |       +------+
 *                                  +-----> |  /2  | -----> APB1 (36MHz)
 *                                  |       +------+
 *                                  |
 *                                  +-----> APB2 (72MHz)
 */


#define  CLOCK_HSE_OSC_HZ            (8000000)
#define  CLOCK_PLLMUL                (9)
#define  CLOCK_PLL_OUT_HZ            (72000000)
#define  CLOCK_PLL_IN_HZ             (CLOCK_HSE_OSC_HZ * CLOCK_PLLMUL)


/**
 *  @brief  SRAM memory information
*/
#define  SRAM_MAIN_START_ADDR        (0x20000000)
#define  SRAM_MAIN_SIZE              (CONFIG_SRAM_SIZE)


uint32_t system_core_clock = CLOCK_PLL_OUT_HZ;


static void clock_init(void)
{
    volatile uint32_t counter = 0, status = 0;

	u16 retry=0;
    /* Reset the RCC clock configuration to the default reset state(for debug purpose) */
    /* Set HSION bit */
    RCC->CR |= (uint32_t)0x00000001;

    RCC->CFGR &= (uint32_t)0xF8FF0000;

    /* Reset HSEON, CSSON and PLLON bits */
    RCC->CR &= (uint32_t)0xFEF6FFFF;

    /* Reset HSEBYP bit */
    RCC->CR &= (uint32_t)0xFFFBFFFF;

    /* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
    RCC->CFGR &= (uint32_t)0xFF80FFFF;

    /* Disable all interrupts and clear pending bits  */
	RCC->CIR = 0x009F0000;

    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/    
    /* Enable HSE */    
    RCC->CR |= ((uint32_t)RCC_CR_HSEON);

    /* Wait till HSE is ready and if Time out is reached exit */
    do {
        status = RCC->CR & RCC_CR_HSERDY;
        counter++;  
    } while((status == 0) && (counter != HSE_STARTUP_TIMEOUT));

    if ((RCC->CR & RCC_CR_HSERDY) != RESET) {
        status = (uint32_t)0x01;
    }
    else {
        status = (uint32_t)0x00;
    }  

    if (status == (uint32_t)0x01) {
        /* Enable Prefetch Buffer */
        FLASH->ACR |= FLASH_ACR_PRFTBE;

        /* Flash 2 wait state */
        FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
        FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_2;    


        /* HCLK = SYSCLK */
        RCC->CFGR &= ~(uint32_t)(0xF << 4);
        RCC->CFGR |= (uint32_t)(0 << 4);
        
        /* PCLK2 = HCLK */
        RCC->CFGR &= ~(uint32_t)(0x7 << 11);
        RCC->CFGR |= (uint32_t)(0 << 11);
        
        /* PCLK1 = HCLK  / 2 */
        RCC->CFGR &= ~(uint32_t)(0x7 << 8);
        RCC->CFGR |= (uint32_t)(4 << 8);

        /* USBCLK = HCLK / 1.5 */
        RCC->CFGR &= ~(uint32_t)(1 << 22);

        /*  PLL configuration: PLLCLK = HSE * 9 = 72 MHz */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLXTPRE |
            RCC_CFGR_PLLMULL));

        RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLSRC_HSE | ((CLOCK_PLLMUL - 2) << 18));

        /* Enable PLL */
        RCC->CR |= RCC_CR_PLLON;

        /* Wait till PLL is ready */
        while((RCC->CR & RCC_CR_PLLRDY) == 0){
            /* Wait for PLL to be ready */
        }
        
        /* Select PLL as system clock source */
        RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
        RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;    
    
        /* Wait till PLL is used as system clock source */
        while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08) {
            /* Wait for PLL to be used as system clock source */
        }
    }

    /* Vector Table Relocation in Internal FLASH. */
    SCB->VTOR = FLASH_BASE;
}


extern size_t eb_system_heap;

static void heap_init(void)
{
    void* start = &eb_system_heap;
	kmem_init(start, CONFIG_SRAM_SIZE - CONFIG_STACK_SIZE - ((uint32_t)start - SRAM_MAIN_START_ADDR));
}


void eb_machine_init(void)
{
	clock_init();
	heap_init();
}
